package service;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import service.mappers.Context;
import service.mappers.Host;
import service.mappers.Wrapper;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author: jie.zhao
 * @create: 2021/03/20 18:24
 */
public class Bootstrap {

    private Host host;

//    private Map<String, AbstractHttpServlet> map  = new HashMap<>();
//
//    private int port = 8080;
//
//    public int getPort() {
//        return port;
//    }
//
//    public void setPort(int port) {
//        this.port = port;
//    }

    public void start() throws Exception {
        parseServer();

        ServerSocket serverSocket = new ServerSocket(host.getPort());

        System.out.println("socket启动");

//        while (true){
//            Socket socket = serverSocket.accept();
//            OutputStream outputStream = socket.getOutputStream();
//            String content = "hello world";
//            String header = HttpProtocolUtil.getHeader200(content.getBytes().length);
//            outputStream.write((header+content).getBytes());
//            socket.close();
//        }

//        while (true){
//            Socket socket = serverSocket.accept();
//            final InputStream inputStream = socket.getInputStream();
//            int count = 0;
//            while (count==0){
//                count = inputStream.available();
//            }
//
//            byte[] arr = new byte[count];
//            inputStream.read(arr);
//            System.out.println(new String(arr));
//                        socket.close();
//
//        }

//        while (true){
//            Socket socket = serverSocket.accept();
//            final InputStream inputStream = socket.getInputStream();
//            Request request = new Request(inputStream);
//            Response response = new Response(socket.getOutputStream());
//            response.outPutHtml(request.getUrl());
//
//            socket.close();
//
//        }
        // 解析server.xml

        while (true){
            Socket socket = serverSocket.accept();
            final InputStream inputStream = socket.getInputStream();
            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());
            String url = request.getUrl();
            String ip = request.getIp();
            int port = request.getPort();
            String method = request.getMethod();
            String projectName = getProjectName(url);
            String servletUrl = getServletUrl(url);
            if(ip.equals(host.getIp()) && port == host.getPort()) {
                AtomicReference<Boolean> isReturn404 = new AtomicReference<>(true);
                List<Context> contextList = host.getContextList();
                contextList.forEach(context -> {
                    if(context.getProjectName().equals(projectName)){
                        Wrapper wrapper = context.getWrapper();
                        Map<String, AbstractHttpServlet> map = wrapper.getMap();
                        AbstractHttpServlet httpServlet = map.get(servletUrl);
                        if(httpServlet!=null) {
                            try {
                                isReturn404.set(false);
                                httpServlet.service(request, response);

                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });
                if(isReturn404.get()){
                    response.outPutHtml(request.getUrl());
                }
            }else {
                response.outPutHtml(request.getUrl());
            }

            socket.close();

        }


    }

    private String getProjectName(String url) {
        return url.substring(1,url.indexOf("/",1));
    }

    private String getServletUrl(String url){
        return url.substring(url.indexOf("/",1));
    }

    private void parseServer() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();

        try {
            Document read = saxReader.read(resourceAsStream);
            Element rootElement = read.getRootElement();
            List<Element> list = rootElement.selectNodes("//Service");
            if(list!=null && list.size()>0){
                Element serviceElement = list.get(0);
                List<Element> connectorNodes = serviceElement.selectNodes("//Connector");
                if(connectorNodes!=null && connectorNodes.size()>0){
                    Element connectorNode = connectorNodes.get(0);
                    int port = Integer.parseInt(connectorNode.attributeValue("port"));
                    this.host = new Host(port);

                    List<Element> engineList = connectorNode.selectNodes("//Engine");
                    if(engineList!=null && engineList.size()>0){
                        Element engine = engineList.get(0);
                        Element hostNode = (Element) engine.selectSingleNode("//Host");
                        if(hostNode!=null){
                            String ip =  hostNode.attributeValue("name");
                            host.setIp(ip);
                            String fileName = hostNode.attributeValue("appBase");
                            initServlet(fileName);
                        }
                    }


                }


            }


        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }

    private void initServlet(String fileName) throws FileNotFoundException {
        File file = new File(fileName);
        File[] files = file.listFiles();
        List<Context> contextList = new ArrayList<>(files.length);
        for (int i = 0; i < files.length; i++) {
            File projectFile = files[i];
            String projectName =  projectFile.getName();
            Context context = new Context(projectName);
            contextList.add(context);
            File[] listFiles = projectFile.listFiles();
            for (int i1 = 0; i1 < listFiles.length; i1++) {
                if(listFiles[i1].getName().equals("web.xml")){
                    Map<String, AbstractHttpServlet> servletMap = loadServlet(new FileInputStream(listFiles[i1]));
                    Wrapper wrapper = new Wrapper(servletMap);
                    context.setWrapper(wrapper);
                    break;
                }
            }
        }
        host.setContextList(contextList);
    }

    private Map<String, AbstractHttpServlet> loadServlet(InputStream resourceAsStream) {
        Map<String, AbstractHttpServlet> servletMap =  new HashMap<>();
        SAXReader saxReader = new SAXReader();

        try {
            Document read = saxReader.read(resourceAsStream);
            Element rootElement = read.getRootElement();
            List<Element> list = rootElement.selectNodes("//servlet");
            for (int i = 0; i < list.size(); i++) {
                Element element = list.get(i);
                Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
                String stringValue = servletNameElement.getStringValue();
                Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
                String classValue = servletClassElement.getStringValue();


                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='"+stringValue+"']");
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

                servletMap.put(urlPattern, (AbstractHttpServlet) Class.forName(classValue).newInstance());


            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return servletMap;
    }

//    private void loadServlet() {
//         InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("webapps/demo2/web.xml");
//         SAXReader saxReader = new SAXReader();
//
//        try {
//             Document read = saxReader.read(resourceAsStream);
//             Element rootElement = read.getRootElement();
//             List<Element> list = rootElement.selectNodes("//servlet");
//            for (int i = 0; i < list.size(); i++) {
//                Element element = list.get(i);
//                Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
//                 String stringValue = servletNameElement.getStringValue();
//                Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
//                String classValue = servletClassElement.getStringValue();
//
//
//                 Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='"+stringValue+"']");
//                 String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
//
//                map.put(urlPattern, (AbstractHttpServlet) Class.forName(classValue).newInstance());
//
//
//            }
//        } catch (DocumentException e) {
//            e.printStackTrace();
//        } catch (IllegalAccessException e) {
//            e.printStackTrace();
//        } catch (InstantiationException e) {
//            e.printStackTrace();
//        } catch (ClassNotFoundException e) {
//            e.printStackTrace();
//        }
//    }


    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
